"""Shared bootstrap helpers for standalone scripts.

API contract (used by generated scripts via `from src.bootstrap_helpers import ...`):
- start(label: Optional[str]) -> float
- end(start_time: float, label: Optional[str]) -> None
- progress(...) : contextmanager wrapper around rich.Progress
- set_plot_style(style_cfg: Dict[str, Any]) -> None
- detect_chinese_font(...) -> Optional[str]
- force_chinese_font(ax, font_family: str|path) -> None
- print_summary_table(rows, columns=None, title="运行摘要") -> None
- log_saved(path, label) -> None
- Parallel, delayed : joblib-compatible shims (if joblib installed)

The content is adapted from the embedded snippet to allow one-line imports in generated scripts.
"""
from pathlib import Path
import os
from typing import Optional, Dict, Any

from rich.console import Console
from rich.progress import Progress, SpinnerColumn, BarColumn, TextColumn, TimeElapsedColumn, MofNCompleteColumn
from rich.panel import Panel
from rich.align import Align
from rich.table import Table
from rich import box

console = Console()


def get_logger(name: str = "gmt-template", level: str = "INFO"):
    """Return a configured stdlib logger that integrates with rich for pretty output.

    Usage:
        logger = get_logger(__name__, level=os.environ.get('LOG_LEVEL','INFO'))
    """
    import logging
    try:
        from rich.logging import RichHandler
        handler = RichHandler()
    except Exception:
        handler = None
    logger = logging.getLogger(name)
    if not logger.handlers:
        if handler:
            logger.addHandler(handler)
        else:
            logger.addHandler(logging.StreamHandler())
    logger.setLevel(getattr(logging, level.upper(), logging.INFO))
    return logger


def load_config_from_file(path: str = "config.yaml") -> dict:
    """Load YAML config from path if exists, otherwise return empty dict.
    This is a tiny helper used by templates to implement config-file-first behavior.
    """
    try:
        p = Path(path)
        if not p.exists():
            return {}
        try:
            import yaml
        except Exception:
            return {}
        with p.open('r', encoding='utf-8') as f:
            return yaml.safe_load(f) or {}
    except Exception:
        return {}


def set_chinese_font(font_family: str = "Heiti TC") -> None:
    try:
        import matplotlib.pyplot as plt
        plt.rcParams['font.sans-serif'] = [font_family]
        plt.rcParams['axes.unicode_minus'] = False
        console.log(f"字体配置已加载：{plt.rcParams.get('font.sans-serif')}")
    except Exception as e:
        console.log(f"设置中文字体时出错（共用模块）：{e}")


def detect_chinese_font(preferred: list[str] | None = None) -> str | None:
    try:
        import matplotlib.font_manager as fm
        if preferred is None:
            preferred = [
                'Heiti TC', 'PingFang SC', 'STHeiti', 'Songti SC', 'Hiragino Sans GB',
                'Microsoft YaHei', 'SimHei', 'Noto Sans CJK SC', 'PingFang', 'Apple LiGothic'
            ]
        family_names = set()
        for font_path in fm.findSystemFonts(fontpaths=None, fontext='ttf') + fm.findSystemFonts(fontpaths=None, fontext='otf'):
            try:
                fp = fm.FontProperties(fname=font_path)
                name = fp.get_name()
                if name:
                    family_names.add(name)
            except Exception:
                continue
        for name in preferred:
            if name in family_names:
                return name
        for candidate in family_names:
            low = candidate.lower()
            if any(key in low for key in ('hei', 'song', 'fang', 'noto', 'yahei', 'simhei', 'li')):
                return candidate
    except Exception as e:
        console.log(f"detect_chinese_font 出错（共用模块）：{e}")
    return None


def set_plot_style(style_cfg: Dict[str, Any] = None) -> None:
    import matplotlib
    import matplotlib.pyplot as plt
    if style_cfg is None:
        style_cfg = {'style': 'darkgrid', 'dpi': 300, 'figsize': [8,6], 'bbox_inches': 'tight', 'font_family':'Heiti TC'}
    preferred = [style_cfg.get('font_family', 'Heiti TC')]
    detected = detect_chinese_font(preferred)
    font_family = detected or style_cfg.get('font_family', 'Heiti TC')
    matplotlib.rcParams['font.family'] = font_family
    matplotlib.rcParams['font.sans-serif'] = [font_family]
    matplotlib.rcParams['axes.unicode_minus'] = False
    try:
        import seaborn as sns
        sns.set_style(style_cfg.get('style', 'darkgrid'))
    except Exception:
        matplotlib.style.use(style_cfg.get('style', 'default'))
    plt.rcParams['figure.dpi'] = style_cfg.get('dpi', 300)
    plt.rcParams['figure.figsize'] = style_cfg.get('figsize', [8,6])
    plt.rcParams['savefig.bbox'] = style_cfg.get('bbox_inches', 'tight')


def force_chinese_font(ax, font_family: str = 'Heiti TC'):
    try:
        import matplotlib.font_manager as fm
        import matplotlib as mpl
        from matplotlib.font_manager import FontProperties
        font_path = None
        if isinstance(font_family, str) and (font_family.endswith('.ttf') or font_family.endswith('.otf') or '/' in font_family or '\\' in font_family):
            font_path = font_family
        else:
            try:
                prop = FontProperties(family=font_family)
                font_path = fm.findfont(prop)
            except Exception:
                font_path = None
        fp = FontProperties(fname=font_path) if font_path else None
        texts = []
        texts.extend(ax.get_xticklabels())
        texts.extend(ax.get_yticklabels())
        try:
            texts.append(ax.xaxis.label)
            texts.append(ax.yaxis.label)
            texts.append(ax.title)
        except Exception:
            pass
        for t in texts:
            try:
                if fp:
                    t.set_fontproperties(fp)
                else:
                    t.set_fontfamily(font_family)
            except Exception:
                pass
        legend = ax.get_legend()
        if legend:
            try:
                if fp:
                    try:
                        legend.get_title().set_fontproperties(fp)
                    except Exception:
                        pass
                    for text in legend.get_texts():
                        try:
                            text.set_fontproperties(fp)
                        except Exception:
                            pass
                else:
                    for text in legend.get_texts():
                        try:
                            text.set_fontfamily(font_family)
                        except Exception:
                            pass
            except Exception:
                pass
    except Exception as e:
        console.log(f"force_chinese_font 共用模块出错：{e}")


def font_path_for_family(family_name: str) -> str | None:
    try:
        import matplotlib.font_manager as fm
        from matplotlib.font_manager import FontProperties
        prop = FontProperties(family=family_name)
        return fm.findfont(prop)
    except Exception:
        return None


def start(label: Optional[str] = None) -> float:
    import time
    label = f" {label}" if label else ""
    start_time_str = time.strftime('%Y-%m-%d %H:%M:%S')
    start_time = time.time()
    try:
        panel = Panel(Align.center(f"🚀 开始{label}\n{start_time_str}", vertical="middle"), title="开始", border_style="green")
        console.print(panel)
    except Exception:
        console.print(start_time_str)
    return start_time


def end(start_time: float, label: Optional[str] = None) -> None:
    import time
    label = f" {label}" if label else ""
    end_time_str = time.strftime('%Y-%m-%d %H:%M:%S')
    duration = time.time() - start_time
    try:
        secs = int(duration)
        hours, rem = divmod(secs, 3600)
        mins, secs = divmod(rem, 60)
        hhmmss = f"{hours:02d}:{mins:02d}:{secs:02d}"
        human = []
        if hours:
            human.append(f"{hours}小时")
        if mins:
            human.append(f"{mins}分")
        human.append(f"{secs}秒")
        human_str = ''.join(human)
        panel = Panel(Align.center(f"✅ 结束{label}\n{end_time_str}\n耗时 {human_str}（{hhmmss}）", vertical="middle"), title="结束", border_style="bright_green")
        console.print(panel)
    except Exception:
        console.print(f"结束{label} {end_time_str}，耗时 {int(duration)} 秒")


def print_summary_table(rows: list[tuple], columns: list[str] | None = None, title: str = "运行摘要") -> None:
    try:
        table = Table(title=title, box=box.SIMPLE_HEAVY)
        if not columns:
            if rows and len(rows) > 0:
                columns = [f"列{i+1}" for i in range(len(rows[0]))]
            else:
                columns = []
        for col in columns:
            table.add_column(col, style="cyan", no_wrap=True)
        def fmt(x):
            try:
                if isinstance(x, float):
                    return f"{x:.3f}"
                import numbers
                if isinstance(x, numbers.Real) and not isinstance(x, bool):
                    return f"{float(x):.3f}"
            except Exception:
                pass
            return str(x)
        for r in rows:
            table.add_row(*[fmt(x) for x in r])
        console.print(table)
    except Exception as e:
        console.log(f"打印摘要表出错（共用模块）：{e}")


def log_saved(path: str | Path | os.PathLike, label: Optional[str] = None) -> None:
    try:
        from datetime import datetime
        p = Path(path)
        ts = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        size = None
        if p.exists() and p.is_file():
            try:
                size_bytes = p.stat().st_size
                if size_bytes < 1024:
                    size = f"{size_bytes} B"
                elif size_bytes < 1024**2:
                    size = f"{size_bytes/1024:.1f} KB"
                else:
                    size = f"{size_bytes/1024**2:.2f} MB"
            except Exception:
                size = None
        label = f" {label}" if label else ""
        suffix = p.suffix.lower()
        if suffix in ('.png', '.jpg', '.jpeg', '.gif'):
            emoji = '🖼️'
        else:
            emoji = '💾'
        body = f"{emoji} 已保存{label}\n路径: {p}\n时间: {ts}"
        if size:
            body += f"\n大小: {size}"
        panel = Panel(Align.left(body), title="已保存", border_style="blue")
        console.print(panel)
    except Exception as e:
        console.log(f"log_saved 出错（共用模块）：{e}")


def progress(*, total: int = 100, **kwargs) -> Progress:
    spinner_style = kwargs.pop('spinner_style', 'green')
    complete_style = kwargs.pop('complete_style', 'bright_green')
    description_template = kwargs.pop('description_template', '[bold]{task.description}')
    show_spinner = kwargs.pop('show_spinner', True)
    columns = []
    if show_spinner:
        columns.append(SpinnerColumn(style=spinner_style))
    columns.append(TextColumn(description_template))
    columns.append(BarColumn(bar_width=None, complete_style=complete_style))
    columns.append(MofNCompleteColumn())
    columns.append(TimeElapsedColumn())
    return Progress(*columns, **kwargs)


# joblib compatibility shim
try:
    from joblib import Parallel, delayed
    def _joblib_available():
        return True
except Exception:
    def _joblib_available():
        return False
    def delayed(func, *dargs, **dkwargs):
        def wrapper(*args, **kwargs):
            return func(*args, **kwargs)
        return wrapper
    class Parallel:
        def __init__(self, n_jobs=1, **kwargs):
            self.n_jobs = n_jobs
        def __call__(self, tasks):
            res = []
            for t in tasks:
                try:
                    res.append(t())
                except Exception as e:
                    res.append(e)
            return res
